package com.jl15988.mybatispluskit.join.wrapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.jl15988.mybatispluskit.join.enums.JoinType;
import com.jl15988.mybatispluskit.join.model.JoinInfo;
import com.jl15988.mybatispluskit.join.utils.LambdaUtils;
import com.jl15988.mybatispluskit.join.utils.TableInfoUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * Lambda 关联查询包装器
 * 扩展 MyBatis-Plus 的 LambdaQueryWrapper，支持 Lambda 表达式的表关联查询
 *
 * @param <T> 主表实体类型
 * @author Jalon
 * @since 2025/1/1
 */
public class JoinLambdaWrapper<T> extends QueryWrapper<T> {

    /**
     * 关联信息列表
     */
    private final List<JoinInfo> joinInfos = new ArrayList<>();

    /**
     * 主表别名
     */
    private String mainTableAlias = "t";

    /**
     * 当前别名计数器
     */
    private int aliasCounter = 0;

    /**
     * 自定义查询字段 SQL
     */
    private String selectSql;

    /**
     * 默认构造函数
     */
    public JoinLambdaWrapper() {
        super();
    }

    /**
     * 构造函数
     *
     * @param entity 实体对象
     */
    public JoinLambdaWrapper(T entity) {
        super(entity);
    }

    /**
     * 设置主表别名
     *
     * @param alias 主表别名
     * @return 当前包装器实例
     */
    public JoinLambdaWrapper<T> setMainTableAlias(String alias) {
        this.mainTableAlias = alias;
        return this;
    }

    /**
     * 设置查询字段
     *
     * @param columns 查询字段，多个字段用逗号分隔
     * @return 当前包装器实例
     */
    public JoinLambdaWrapper<T> select(String columns) {
        this.selectSql = columns;
        return this;
    }

    /**
     * 内连接（智能判断主表关联或级联关联）
     *
     * @param joinEntityClass 关联表实体类
     * @param fromField       源表关联字段（自动推断源表类型）
     * @param joinField       关联表关联字段
     * @param <M>             源表实体类型
     * @param <R>             关联表实体类型
     * @return 当前包装器实例
     */
    public <M, R> JoinLambdaWrapper<T> innerJoin(Class<R> joinEntityClass,
                                                 SFunction<M, ?> fromField,
                                                 SFunction<R, ?> joinField) {
        return smartJoin(JoinType.INNER, joinEntityClass, fromField, joinField);
    }

    /**
     * 左连接（智能判断主表关联或级联关联）
     *
     * @param joinEntityClass 关联表实体类
     * @param fromField       源表关联字段（自动推断源表类型）
     * @param joinField       关联表关联字段
     * @param <M>             源表实体类型
     * @param <R>             关联表实体类型
     * @return 当前包装器实例
     */
    public <M, R> JoinLambdaWrapper<T> leftJoin(Class<R> joinEntityClass,
                                                SFunction<M, ?> fromField,
                                                SFunction<R, ?> joinField) {
        return smartJoin(JoinType.LEFT, joinEntityClass, fromField, joinField);
    }

    /**
     * 右连接（智能判断主表关联或级联关联）
     *
     * @param joinEntityClass 关联表实体类
     * @param fromField       源表关联字段（自动推断源表类型）
     * @param joinField       关联表关联字段
     * @param <M>             源表实体类型
     * @param <R>             关联表实体类型
     * @return 当前包装器实例
     */
    public <M, R> JoinLambdaWrapper<T> rightJoin(Class<R> joinEntityClass,
                                                 SFunction<M, ?> fromField,
                                                 SFunction<R, ?> joinField) {
        return smartJoin(JoinType.RIGHT, joinEntityClass, fromField, joinField);
    }

    /**
     * 全连接（智能判断主表关联或级联关联）
     *
     * @param joinEntityClass 关联表实体类
     * @param fromField       源表关联字段（自动推断源表类型）
     * @param joinField       关联表关联字段
     * @param <M>             源表实体类型
     * @param <R>             关联表实体类型
     * @return 当前包装器实例
     */
    public <M, R> JoinLambdaWrapper<T> fullJoin(Class<R> joinEntityClass,
                                                SFunction<M, ?> fromField,
                                                SFunction<R, ?> joinField) {
        return smartJoin(JoinType.FULL, joinEntityClass, fromField, joinField);
    }

    /**
     * 智能连接方法（自动判断主表关联或级联关联）
     *
     * @param joinType        连接类型
     * @param joinEntityClass 关联表实体类
     * @param fromField       源表关联字段（自动推断源表类型）
     * @param joinField       关联表关联字段
     * @param <M>             源表实体类型
     * @param <R>             关联表实体类型
     * @return 当前包装器实例
     */
    private <M, R> JoinLambdaWrapper<T> smartJoin(JoinType joinType,
                                                  Class<R> joinEntityClass,
                                                  SFunction<M, ?> fromField,
                                                  SFunction<R, ?> joinField) {
        // 从 Lambda 表达式推断源表类型
        Class<M> fromEntityClass = LambdaUtils.getEntityClass(fromField);

        // 确定源表别名
        String fromTableAlias;

        // 如果源表是主表类型，使用主表别名
        if (fromEntityClass.equals(getEntityClass())) {
            fromTableAlias = mainTableAlias;
        } else {
            // 否则，获取源表别名
            fromTableAlias = getTableAlias(fromEntityClass);
            if (fromTableAlias == null) {
                throw new IllegalArgumentException("源表 " + fromEntityClass.getSimpleName() + " 尚未关联，请先关联源表");
            }
        }

        // 创建新的关联表信息
        String joinTableName = TableInfoUtils.getTableName(joinEntityClass);
        String joinTableAlias = "t" + (++aliasCounter);
        String fromFieldName = LambdaUtils.getColumnName(fromField);
        String joinFieldName = LambdaUtils.getColumnName(joinField);

        JoinInfo joinInfo = new JoinInfo(joinType, fromTableAlias, joinTableName,
                joinTableAlias, fromFieldName, joinFieldName, joinEntityClass);
        joinInfos.add(joinInfo);

        return this;
    }

    /**
     * 获取表的别名（支持主表和关联表）
     *
     * @param entityClass 实体类
     * @return 表别名
     */
    private String getTableAlias(Class<?> entityClass) {
        // 先检查关联表
        for (JoinInfo joinInfo : joinInfos) {
            if (joinInfo.getJoinEntityClass().equals(entityClass)) {
                return joinInfo.getJoinTableAlias();
            }
        }

        // 检查是否是主表
//        Class<T> mainEntityClass = getEntityClass();
//        System.out.println(mainEntityClass.getName());
//        if (mainEntityClass != null && mainEntityClass.equals(entityClass)) {
//            return mainTableAlias;
//        } else {
//            // 如果无法获取主表类型，假设第一次查询的就是主表
//            // 这是一个降级策略，在大多数情况下应该能正常工作
//            if (joinInfos.isEmpty()) {
//                return mainTableAlias;
//            }
//        }
//
//        return null;
        return mainTableAlias;
    }

    /**
     * 获取lambda字段名
     *
     * @param column lambda字段
     * @param <R>    实体
     * @return lambda字段名
     */
    private <R> String getAliasColumnName(SFunction<R, ?> column) {
        Class<R> entityClass = LambdaUtils.getEntityClass(column);
        String tableAlias = getTableAlias(entityClass);
        String columnName = LambdaUtils.getColumnName(column);
        if (tableAlias != null) {
            return tableAlias + "." + columnName;
        }
        return columnName;
    }

    /**
     * 获取实体类（主表类型）
     *
     * @return 主表实体类
     */
    public Class<T> getEntityClass() {
        return super.getEntityClass();
    }

    /**
     * 获取关联信息列表
     *
     * @return 关联信息列表
     */
    public List<JoinInfo> getJoinInfos() {
        return joinInfos;
    }

    /**
     * 获取主表别名
     *
     * @return 主表别名
     */
    public String getMainTableAlias() {
        return mainTableAlias;
    }

    /**
     * 获取查询字段 SQL
     *
     * @return 查询字段 SQL
     */
    public String getSelectSql() {
        return StringUtils.isNotBlank(selectSql) ? selectSql : mainTableAlias + ".*";
    }

    /**
     * 获取已关联表的别名（用于调试或特殊需求）
     *
     * @param joinEntityClass 关联表实体类
     * @return 表别名，如果未找到返回 null
     */
    public String getJoinTableAlias(Class<?> joinEntityClass) {
        return getTableAlias(joinEntityClass);
    }

    // ==================== 重写父类方法，确保返回类型为 JoinLambdaWrapper ====================

    public <R> JoinLambdaWrapper<T> eq(SFunction<R, ?> column, Object val) {
        super.eq(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> ne(SFunction<R, ?> column, Object val) {
        super.ne(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> gt(SFunction<R, ?> column, Object val) {
        super.gt(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> ge(SFunction<R, ?> column, Object val) {
        super.ge(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> lt(SFunction<R, ?> column, Object val) {
        super.lt(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> le(SFunction<R, ?> column, Object val) {
        super.le(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> like(SFunction<R, ?> column, Object val) {
        super.like(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> notLike(SFunction<R, ?> column, Object val) {
        super.notLike(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> likeLeft(SFunction<R, ?> column, Object val) {
        super.likeLeft(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> likeRight(SFunction<R, ?> column, Object val) {
        super.likeRight(this.getAliasColumnName(column), val);
        return this;
    }

    public <R> JoinLambdaWrapper<T> isNull(SFunction<R, ?> column) {
        super.isNull(this.getAliasColumnName(column));
        return this;
    }

    public <R> JoinLambdaWrapper<T> isNotNull(SFunction<R, ?> column) {
        super.isNotNull(this.getAliasColumnName(column));
        return this;
    }

    public <R> JoinLambdaWrapper<T> in(SFunction<R, ?> column, Object... values) {
        super.in(this.getAliasColumnName(column), values);
        return this;
    }

    public <R> JoinLambdaWrapper<T> notIn(SFunction<R, ?> column, Object... values) {
        super.notIn(this.getAliasColumnName(column), values);
        return this;
    }

    public <R> JoinLambdaWrapper<T> between(SFunction<R, ?> column, Object val1, Object val2) {
        super.between(this.getAliasColumnName(column), val1, val2);
        return this;
    }

    public <R> JoinLambdaWrapper<T> notBetween(SFunction<R, ?> column, Object val1, Object val2) {
        super.notBetween(this.getAliasColumnName(column), val1, val2);
        return this;
    }

    public <R> JoinLambdaWrapper<T> orderByAsc(SFunction<R, ?> column) {
        super.orderByAsc(this.getAliasColumnName(column));
        return this;
    }

    public <R> JoinLambdaWrapper<T> orderByDesc(SFunction<R, ?> column) {
        super.orderByDesc(this.getAliasColumnName(column));
        return this;
    }

    public <R> JoinLambdaWrapper<T> groupBy(SFunction<R, ?> column) {
        super.groupBy(this.getAliasColumnName(column));
        return this;
    }

    @Override
    public JoinLambdaWrapper<T> having(String sqlHaving, Object... params) {
        super.having(sqlHaving, params);
        return this;
    }
} 